use sea_orm::{ActiveValue, DatabaseConnection, DbErr};
use sky_common::constant::{self, status};

use crate::mapper::{self};
use sky_pojo::{
    dto::{
        category,
        setmeal::{SetMealQueryDTO, SetmealDTO},
    },
    entities::setmeal,
    vo::{self, dish::DishItemVO, setmeal::SetmealVO, PageResult},
};

pub async fn query_by_id(db: DatabaseConnection, id: i64) -> Result<SetmealVO, String> {
    let result = mapper::setmeal::query_by_id(db.clone(), id).await;

    match result {
        Ok(model) => {
            let mut vo = SetmealVO::from(model.clone());

            let dishes_res = mapper::setmeal::query_by_setmeal_dishes(db, model.id).await;

            let dishes = match dishes_res {
                Ok(dishes) => dishes,
                Err(e) => return Err(e.to_string()),
            };

            vo.setmeal_dishes.extend(dishes);

            Ok(vo)
        }
        Err(e) => Err(e.to_string()),
    }
}

pub async fn save_with_dishes(
    db: DatabaseConnection,
    dto: SetmealDTO,
    creator: i64,
) -> Result<i64, String> {
    let model = setmeal::ActiveModel {
        create_user: ActiveValue::set(Some(creator)),
        update_user: ActiveValue::set(Some(creator)),
        name: dto
            .name
            .map(ActiveValue::set)
            .expect("The name must be set during creation"),
        category_id: dto
            .category_id
            .map(ActiveValue::set)
            .expect("The name must be set during creation"),
        price: dto
            .price
            .map(ActiveValue::set)
            .expect("The name must be set during creation"),
        status: ActiveValue::set(Some(status::ENABLE)),
        description: dto
            .description
            .map(ActiveValue::set)
            .unwrap_or(ActiveValue::not_set())
            .into(),
        image: dto
            .image
            .map(ActiveValue::set)
            .expect("The image must be set during creation")
            .into(),
        ..Default::default()
    };

    let model = mapper::setmeal::create(db.clone(), model).await.unwrap();

    let _flavors = mapper::setmeal::insert_setmeal_dishes(db, dto.setmeal_dishes, model.id)
        .await
        .unwrap();

    Ok(model.id)
}

pub async fn query(
    db: DatabaseConnection,
    dto: SetMealQueryDTO,
) -> Result<PageResult<setmeal::Model>, String> {
    mapper::setmeal::query(db, dto)
        .await
        .map_err(|e| e.to_string())
}

pub async fn enable_disable(
    db: DatabaseConnection,
    dto: SetmealDTO,
    modifier: i64,
) -> Result<SetmealVO, String> {
    if dto.status == Some(constant::status::ENABLE) {
        let id = dto.id.unwrap();
        let dishes_res = mapper::dish::get_by_setmeal_id(db.clone(), id).await;
        match dishes_res {
            Ok(dishes) => {
                for dish in dishes {
                    if dish.status == Some(constant::status::DISABLE) {
                        // 如果有禁用的菜品，返回错误
                        return Err("套餐内包含未启售菜品，无法启售".to_string());
                    }
                }
            }
            Err(e) => return Err(e.to_string()),
        }
    }

    self::modify(db, dto, modifier).await
}

pub async fn modify(
    db: DatabaseConnection,
    dto: SetmealDTO,
    modifier: i64,
) -> Result<SetmealVO, String> {
    let setmeal_id = match dto.id {
        Some(id) => id,
        None => return Err("id required during modification".to_owned()),
    };
    let model = setmeal::ActiveModel {
        category_id: dto
            .category_id
            .map(ActiveValue::set)
            .unwrap_or(ActiveValue::NotSet),
        name: dto
            .name
            .map(ActiveValue::set)
            .unwrap_or(ActiveValue::NotSet),
        price: dto
            .price
            .map(ActiveValue::set)
            .unwrap_or(ActiveValue::NotSet),
        status: dto
            .status
            .map(ActiveValue::set)
            .unwrap_or(ActiveValue::NotSet)
            .into(),
        description: dto
            .description
            .map(ActiveValue::set)
            .unwrap_or(ActiveValue::NotSet)
            .into(),
        image: dto
            .image
            .map(ActiveValue::set)
            .unwrap_or(ActiveValue::NotSet)
            .into(),
        update_user: ActiveValue::set(Some(modifier)),
        ..Default::default()
    };

    let model = match mapper::setmeal::modify_by_id(db.clone(), setmeal_id, model).await {
        Ok(model) => model,
        Err(e) => return Err(e.to_string()),
    };

    let vo = SetmealVO::from(model);

    Ok(vo)
}

pub async fn modify_widh_dishes(
    db: DatabaseConnection,
    dto: SetmealDTO,
    modifier: i64,
) -> Result<SetmealVO, String> {
    let setmeal_id = match dto.id {
        Some(id) => id,
        None => return Err("id required during modification".to_owned()),
    };
    let model = setmeal::ActiveModel {
        category_id: dto
            .category_id
            .map(ActiveValue::set)
            .unwrap_or(ActiveValue::NotSet),
        name: dto
            .name
            .map(ActiveValue::set)
            .unwrap_or(ActiveValue::NotSet),
        price: dto
            .price
            .map(ActiveValue::set)
            .unwrap_or(ActiveValue::NotSet),
        status: dto
            .status
            .map(ActiveValue::set)
            .unwrap_or(ActiveValue::NotSet)
            .into(),
        description: dto
            .description
            .map(ActiveValue::set)
            .unwrap_or(ActiveValue::NotSet)
            .into(),
        image: dto
            .image
            .map(ActiveValue::set)
            .unwrap_or(ActiveValue::NotSet)
            .into(),
        update_user: ActiveValue::set(Some(modifier)),
        ..Default::default()
    };
    let model = match mapper::setmeal::modify_by_id(db.clone(), setmeal_id, model).await {
        Ok(model) => model,
        Err(e) => return Err(e.to_string()), // 错误时直接返回
    };

    let mut dishes =
        match mapper::setmeal::dish_modify_branches(db.clone(), setmeal_id, dto.setmeal_dishes)
            .await
        {
            Ok(dishes) => dishes,
            Err(e) => return Err(e.to_string()), // 错误时直接返回
        };

    let mut vo = SetmealVO::from(model);

    vo.setmeal_dishes.append(&mut dishes);

    Ok(vo)
}

pub async fn delete_branch(db: DatabaseConnection, ids: Vec<i64>) -> vo::setmeal::DeleteResult {
    let mut result = vo::setmeal::DeleteResult {
        success: Vec::new(),
        failed: Vec::new(),
    };

    for id in ids {
        let model = match mapper::setmeal::query_by_id(db.clone(), id).await {
            Ok(model) => model,
            Err(_) => {
                result.failed.push(id);
                continue;
            }
        };

        if model.status == Some(status::ENABLE) {
            result.failed.push(id);
        } else {
            match mapper::setmeal::delete_by_setmeal_id(db.clone(), id).await {
                Ok(_) => result.success.push(id),
                Err(_) => result.failed.push(id),
            }
        }
    }
    result
}

// for user
pub async fn list(db: DatabaseConnection, category_id: i64) -> Result<Vec<setmeal::Model>, String> {
    let model = setmeal::ActiveModel {
        category_id: ActiveValue::set(category_id),
        status: ActiveValue::set(Some(constant::status::ENABLE)),
        ..Default::default()
    };

    mapper::setmeal::list(db, model)
        .await
        .map_err(|e| e.to_string())
}

pub async fn get_dish_item_by_id(
    db: DatabaseConnection,
    id: i64,
) -> Result<Vec<DishItemVO>, DbErr> {
    mapper::setmeal::get_dish_item_by_setmeal_id(&db, id).await
}
